"""The Missing math functions.

This library uses ctypes with C math library to extend Python math
capabilities with additional functions and constants.
"""

__author__ = "Ruda Moura <ruda.moura@gmail.com>"
__copyright__ = "Copyright (c) 2010"
__license__ = "3-clause BSD or Python License"
__version__ = "0.0"

import ctypes
#from math import *
from sys import platform as _platform

if _platform == "linux" or _platform == "linux2":
    _libm = ctypes.cdll.LoadLibrary('libm.so.6')
elif _platform == "darwin":
    _libm = ctypes.cdll.LoadLibrary('libSystem.dylib')
else:
    print "Platform", _platform, "is not supported"
    #sys.exit(0)

_libm.nextafter.restype = ctypes.c_double
_libm.nextafter.argtypes = [ctypes.c_double, ctypes.c_double]

_libm.fma.restype = ctypes.c_double
_libm.fma.argtypes = [ctypes.c_double, ctypes.c_double, ctypes.c_double]
_libm.cbrt.restype = ctypes.c_double
_libm.cbrt.argtypes = [ctypes.c_double]
_libm.remainder.restype = ctypes.c_double
_libm.remainder.argtypes = [ctypes.c_double, ctypes.c_double]

_libm.exp2.restyppe = ctypes.c_double
_libm.exp2.argtypes = [ctypes.c_double]
_libm.expm1.restyppe = ctypes.c_double
_libm.expm1.argtypes = [ctypes.c_double]

_libm.log2.restypes = ctypes.c_double 
_libm.log2.argtypes = [ctypes.c_double]

_libm.gamma.restype = ctypes.c_double
_libm.gamma.argtypes = [ctypes.c_double]
_libm.tgamma.restype = ctypes.c_double
_libm.tgamma.argtypes = [ctypes.c_double]
_libm.lgamma.restype = ctypes.c_double
_libm.lgamma.artypes = [ctypes.c_double]

_libm.j0.restype = ctypes.c_double
_libm.j0.argtypes = [ctypes.c_double]
_libm.j1.restype = ctypes.c_double
_libm.j1.argtypes = [ctypes.c_double]
_libm.jn.restype = ctypes.c_double
_libm.jn.argtypes = [ctypes.c_int, ctypes.c_double]

_libm.y0.restype = ctypes.c_double
_libm.y0.argtypes = [ctypes.c_double]
_libm.y1.restype = ctypes.c_double
_libm.y1.argtypes = [ctypes.c_double]
_libm.yn.restype = ctypes.c_double
_libm.yn.argtypes = [ctypes.c_int, ctypes.c_double]

_libm.erf.restype = ctypes.c_double
_libm.erf.argtypes = [ctypes.c_double]
_libm.erfc.restype = ctypes.c_double
_libm.erfc.argtypes = [ctypes.c_double]

_libm.scalbln.restype = ctypes.c_double
_libm.scalbln.argtypes = [ctypes.c_double, ctypes.c_long]

# Math constants as #defined in C
M_E=2.71828182845904523536028747135266250
M_LOG2E=1.44269504088896340735992468100189214
M_LOG10E=0.434294481903251827651128918916605082
M_LN2=0.693147180559945309417232121458176568
M_LN10=2.30258509299404568401799145468436421
M_PI=3.14159265358979323846264338327950288
M_PI_2=1.57079632679489661923132169163975144
M_PI_4=0.785398163397448309615660845819875721
M_1_PI=0.318309886183790671537767526745028724
M_2_PI=0.636619772367581343075535053490057448
M_2_SQRTPI=1.12837916709551257389615890312154517
M_SQRT2=1.41421356237309504880168872420969808
M_SQRT1_2=0.707106781186547524400844362104849039

def nextafter(x, y):
    "Returns the next floating-point number after x in the direction of y."
    return _libm.nextafter(x, y)

def fma(x, y, z):
    "Calculates the value (x*y) + z"
    return _libm.fma(x, y, z)

def cbrt(x):
    "Calculates the cube root of x"
    return _libm.cbrt(x)

def remainder(x, y):
    return _libm.remainder(x, y)

def exp2(x):
    "Calculates 2**x."
    return _libm.exp2(x)

def expm1(x):
    "Calculates e**x - 1."
    return _libm.expm1(x)

def log2(x):
    "Calculates the base-2 logarithm of x."
    return _libm.log2(x)

def gamma(x):
    "Calculates the gamma function of x"
    return _libm.gamma(x)

def tgamma(x):
    "Calculates the gamma function of x"
    return _libm.tgamma(x)

def lgamma(x):
    "Calculates ln of the absolute value of the gamma function of x."
    return _libm.lgamma(x)

def j0(x):
    "Calculates zeroth Bessel function of the first kind evaluated at x"
    return _libm.j0(x)

def j1(x):
    "Calculates first Bessel function of the first kind evaluated at x"
    return _libm.j1(x)

def jn(n, x):
    "Calculates n-th Bessel function of the first kind evaluated at x"
    return _libm.jn(n, x)

def y0(x):
    "Calculates zeroth Bessel function of the second kind evaluated at x"
    return _libm.j0(x)

def y1(x):
    "Calculates first Bessel function of the second kind evaluated at x"
    return _libm.j1(x)

def yn(n, x):
    "Calculates n-th Bessel function of the second kind evaluated at x"
    return _libm.jn(n, x)

def erf(x):
    "Calculates the value of the error function evaluated at x"
    return _libm.erf(x)

def erfc(x):
    "Calculates the value of the the complementary error function evaluated at x"
    return _libm.erfc(x)

def scalbln(x, n):
    "Calculates x*2**n by manipulating the exponent, rather than by actually performing an exponentiation or multiplication"
    return _libm.scalbln(x, n)

def _test():
    assert (nextafter(0, 1) - nextafter(0, 1) == 0)
    assert (erf(0) == 0)
    assert (erfc(0) == 1)
    assert (scalbln(4,32) == 4*2**32)

if __name__ == "__main__":
    _test()
